package android.support.v4.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.FloatRange;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.yanggeng.demo.beginnerguidedemo.R;
import com.yanggeng.demo.beginnerguidedemo.utils.DeviceParams;

import java.lang.ref.WeakReference;

import static android.R.attr.textAppearance;

/**
 * Created by yanggeng on 2017/11/8.
 */

public class ScalePagerStrip extends ViewGroup{

    private static final String TAG = "PagerTitleStrip";

    ViewPager mPager;
    TextView mPrevText;
    TextView mCurrText;
    TextView mNextText;

    private int dotColor; // 指示点颜色

    private int dotRadius; // 指示点半径

    private int textColor; // 文字颜色

    private int textSize; // 文字大小

    private int selectTextColor; // 选中文字颜色

    private int selectTextSize; // 选中文字大小

    private float width, height, prevWidth, currWidth;

    private int mLastKnownCurrentPage = -1;
    float mLastKnownPositionOffset = -1;
    private int mScaledTextSpacing;
    private int mGravity;

    private boolean mUpdatingText;
    private boolean mUpdatingPositions;

    private final PageListener mPageListener = new PageListener();

    private WeakReference<PagerAdapter> mWatchingAdapter;

    private static final int[] ATTRS = new int[] {
            textAppearance,
            android.R.attr.textSize,
            android.R.attr.textColor,
            android.R.attr.gravity
    };

    private static final int[] TEXT_ATTRS = new int[] {
            0x0101038c // android.R.attr.textAllCaps
    };

    private static final float SIDE_ALPHA = 0.6f;
    private static final int TEXT_SPACING = 16; // dip

    private int mNonPrimaryAlpha;
    int mTextColor;

    interface PagerTitleStripImpl {
        void setSingleLineAllCaps(TextView text);
    }

    static class PagerTitleStripImplBase implements PagerTitleStripImpl {
        @Override
        public void setSingleLineAllCaps(TextView text) {
            text.setSingleLine();
        }
    }

    static class PagerTitleStripImplIcs implements PagerTitleStripImpl {
        @Override
        public void setSingleLineAllCaps(TextView text) {
//            PagerTitleStripIcs.setSingleLineAllCaps(text);
        }
    }

    private static final PagerTitleStripImpl IMPL;
    static {
        if (android.os.Build.VERSION.SDK_INT >= 14) {
            IMPL = new PagerTitleStripImplIcs();
        } else {
            IMPL = new PagerTitleStripImplBase();
        }
    }

    private static void setSingleLineAllCaps(TextView text) {
        IMPL.setSingleLineAllCaps(text);
    }

    public ScalePagerStrip(Context context) {
        this(context, null);
    }

    public ScalePagerStrip(Context context, AttributeSet attrs) {
        super(context, attrs);

        addView(mPrevText = new TextView(context));
        addView(mCurrText = new TextView(context));
        addView(mNextText = new TextView(context));

        final TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ScalePagerIndicator, 0, 0);
        int n = typedArray.getIndexCount();

        for (int i = 0; i < n; i ++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.ScalePagerIndicator_dotColor:
                    dotColor = typedArray.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.ScalePagerIndicator_dotRadius:
                    dotRadius = typedArray.getDimensionPixelSize(attr, 10);
                    break;
                case R.styleable.ScalePagerIndicator_indicatorTextColor:
                    textColor = typedArray.getColor(attr, Color.WHITE);
                    mPrevText.setTextColor(this.textColor);
                    mNextText.setTextColor(this.textColor);
                    break;
                case R.styleable.ScalePagerIndicator_indicatorTextSize:
                    textSize = typedArray.getDimensionPixelSize(attr, DeviceParams.sp2px(context, 16));
                    mPrevText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                    mNextText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                    break;
                case R.styleable.ScalePagerIndicator_selectTextColor:
                    selectTextColor = typedArray.getColor(attr, Color.WHITE);
                    mCurrText.setTextColor(selectTextColor);
                    mCurrText.getPaint().setFakeBoldText(true);
                    break;
                case R.styleable.ScalePagerIndicator_selectTextSize:
                    selectTextSize = typedArray.getDimensionPixelSize(attr, DeviceParams.sp2px(context, 22));
                    mCurrText.setTextSize(TypedValue.COMPLEX_UNIT_PX, selectTextSize);
                    break;
            }
        }

        mGravity = Gravity.TOP;
        typedArray.recycle();

        mTextColor = mCurrText.getTextColors().getDefaultColor();
        setNonPrimaryAlpha(SIDE_ALPHA);

//        mPrevText.setEllipsize(TruncateAt.END);
//        mCurrText.setEllipsize(TruncateAt.END);
//        mNextText.setEllipsize(TruncateAt.END);

//        boolean allCaps = false;
//        if (textAppearance != 0) {
//            final TypedArray ta = context.obtainStyledAttributes(textAppearance, TEXT_ATTRS);
//            allCaps = ta.getBoolean(0, false);
//            ta.recycle();
//        }
//
//        if (allCaps) {
//            setSingleLineAllCaps(mPrevText);
//            setSingleLineAllCaps(mCurrText);
//            setSingleLineAllCaps(mNextText);
//        } else {
//            mPrevText.setSingleLine();
//            mCurrText.setSingleLine();
//            mNextText.setSingleLine();
//        }

        final float density = context.getResources().getDisplayMetrics().density;
        mScaledTextSpacing = (int) (TEXT_SPACING * density);

        mPrevText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(mPager.getCurrentItem() - 1);
            }
        });
        mNextText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(mPager.getCurrentItem() + 1);
            }
        });
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
//        int paddingTop = getPaddingTop();
        int prevWidth = mPrevText.getMeasuredWidth();
        int nextWidth = mNextText.getMeasuredWidth();
        int currHeight = mCurrText.getMeasuredHeight();
        float y = mCurrText.getY() + currHeight + dotRadius * 2 + 40;
//        int width = getWidth();
//        int height = getHeight();

        Log.e(TAG, "------width = " + width + ", height = " + height + ", preWidth = " + prevWidth + ", y = " + y);

        Paint paint = new Paint();
        Paint linePaint = new Paint();
        paint.setColor(Color.parseColor("#FFD88E"));
        canvas.drawCircle(width / 2, y, dotRadius + 10, paint);

        paint.setColor(dotColor);

        linePaint.setColor(Color.parseColor("#D8D8D8"));
        linePaint.setStrokeWidth(2);
        if (prevWidth != 0) {
            canvas.drawCircle(paddingLeft + prevWidth / 2, y, dotRadius, paint);
            canvas.drawLine(paddingLeft + prevWidth / 2 + dotRadius + 20, y, width / 2 - dotRadius - 10 - 20, y, linePaint);
        }
        if (nextWidth != 0) {
            canvas.drawCircle(width - paddingRight - nextWidth / 2, y, dotRadius, paint);
            canvas.drawLine(width / 2 + dotRadius + 10 + 20, y, width - paddingRight - nextWidth / 2 - dotRadius - 20, y, linePaint);
        }
        canvas.drawCircle(width / 2, y, dotRadius, paint);
    }

    /**
     * Set the required spacing between title segments.
     *
     * @param spacingPixels Spacing between each title displayed in pixels
     */
    public void setTextSpacing(int spacingPixels) {
        mScaledTextSpacing = spacingPixels;
        requestLayout();
    }

    /**
     * @return The required spacing between title segments in pixels
     */
    public int getTextSpacing() {
        return mScaledTextSpacing;
    }

    /**
     * Set the alpha value used for non-primary page titles.
     *
     * @param alpha Opacity value in the range 0-1f
     */
    public void setNonPrimaryAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
        mNonPrimaryAlpha = (int) (alpha * 255) & 0xFF;
        final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
        mPrevText.setTextColor(transparentColor);
        mNextText.setTextColor(transparentColor);
    }

    /**
     * Set the color value used as the base color for all displayed page titles.
     * Alpha will be ignored for non-primary page titles. See {@link #setNonPrimaryAlpha(float)}.
     *
     * @param color Color hex code in 0xAARRGGBB format
     */
    public void setTextColor(@ColorInt int color) {
        mTextColor = color;
        mCurrText.setTextColor(color);
        final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
        mPrevText.setTextColor(transparentColor);
        mNextText.setTextColor(transparentColor);
    }

    /**
     * Set the default text size to a given unit and value.
     * See {@link TypedValue} for the possible dimension units.
     *
     * <p>Example: to set the text size to 14px, use
     * setTextSize(TypedValue.COMPLEX_UNIT_PX, 14);</p>
     *
     * @param unit The desired dimension unit
     * @param size The desired size in the given units
     */
    public void setTextSize(int unit, float size) {
        mPrevText.setTextSize(unit, size);
        mCurrText.setTextSize(unit, size);
        mNextText.setTextSize(unit, size);
    }

    /**
     * Set the {@link Gravity} used to position text within the title strip.
     * Only the vertical gravity component is used.
     *
     * @param gravity {@link Gravity} constant for positioning title text
     */
    public void setGravity(int gravity) {
        mGravity = gravity;
        requestLayout();
    }

    public void setViewPager(ViewPager viewPager) {
        final PagerAdapter adapter = viewPager.getAdapter();

//        viewPager.setInternalPageChangeListener(mPageListener);
        viewPager.addOnAdapterChangeListener(mPageListener);
        mPager = viewPager;
        updateAdapter(mWatchingAdapter != null ? mWatchingAdapter.get() : null, adapter);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

//        final ViewParent parent = getParent();
//        if (!(parent instanceof ViewPager)) {
//            throw new IllegalStateException(
//                    "PagerTitleStrip must be a direct child of a ViewPager.");
//        }
//
//        final ViewPager pager = (ViewPager) parent;
//        final PagerAdapter adapter = pager.getAdapter();
//
//        pager.setInternalPageChangeListener(mPageListener);
//        pager.addOnAdapterChangeListener(mPageListener);
//        mPager = pager;
//        updateAdapter(mWatchingAdapter != null ? mWatchingAdapter.get() : null, adapter);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mPager != null) {
            updateAdapter(mPager.getAdapter(), null);
            mPager.setInternalPageChangeListener(null);
            mPager.removeOnAdapterChangeListener(mPageListener);
            mPager = null;
        }
    }

    void updateText(int currentItem, PagerAdapter adapter) {
        final int itemCount = adapter != null ? adapter.getCount() : 0;
        mUpdatingText = true;

        CharSequence text = null;
        if (currentItem >= 1 && adapter != null) {
            text = adapter.getPageTitle(currentItem - 1);
        }
        mPrevText.setText(text);

        mCurrText.setText(adapter != null && currentItem < itemCount
                ? adapter.getPageTitle(currentItem) : null);

        text = null;
        if (currentItem + 1 < itemCount && adapter != null) {
            text = adapter.getPageTitle(currentItem + 1);
        }
        mNextText.setText(text);

        // Measure everything
        final int width = getWidth() - getPaddingLeft() - getPaddingRight();
        final int maxWidth = Math.max(0, (int) (width * 0.8f));
        final int childWidthSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
        final int childHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        final int maxHeight = Math.max(0, childHeight);
        final int childHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
        mPrevText.measure(childWidthSpec, childHeightSpec);
        mCurrText.measure(childWidthSpec, childHeightSpec);
        mNextText.measure(childWidthSpec, childHeightSpec);

        mLastKnownCurrentPage = currentItem;

        if (!mUpdatingPositions) {
            updateTextPositions(currentItem, mLastKnownPositionOffset, false);
        }

        mUpdatingText = false;
    }

    @Override
    public void requestLayout() {
        if (!mUpdatingText) {
            super.requestLayout();
        }
    }

    void updateAdapter(PagerAdapter oldAdapter, PagerAdapter newAdapter) {
        if (oldAdapter != null) {
            oldAdapter.unregisterDataSetObserver(mPageListener);
            mWatchingAdapter = null;
        }
        if (newAdapter != null) {
            newAdapter.registerDataSetObserver(mPageListener);
            mWatchingAdapter = new WeakReference<PagerAdapter>(newAdapter);
        }
        if (mPager != null) {
            mLastKnownCurrentPage = -1;
            mLastKnownPositionOffset = -1;
            updateText(mPager.getCurrentItem(), newAdapter);
            requestLayout();
        }
    }

    void updateTextPositions(int position, float positionOffset, boolean force) {
        if (position != mLastKnownCurrentPage) {
            updateText(position, mPager.getAdapter());
        } else if (!force && positionOffset == mLastKnownPositionOffset) {
            return;
        }

        mUpdatingPositions = true;

        final int prevWidth = mPrevText.getMeasuredWidth();
        final int currWidth = mCurrText.getMeasuredWidth();
        final int nextWidth = mNextText.getMeasuredWidth();
        final int halfCurrWidth = currWidth / 2;

        final int stripWidth = getWidth();
        final int stripHeight = getHeight();
        final int paddingLeft = getPaddingLeft();
        final int paddingRight = getPaddingRight();
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        final int textPaddedLeft = paddingLeft + halfCurrWidth;
        final int textPaddedRight = paddingRight + halfCurrWidth;
        final int contentWidth = stripWidth - textPaddedLeft - textPaddedRight;

        float currOffset = positionOffset + 0.5f;
        if (currOffset > 1.f) {
            currOffset -= 1.f;
        }
        final int currCenter = stripWidth - textPaddedRight - (int) (contentWidth * currOffset);
        final int currLeft = currCenter - currWidth / 2;
        final int currRight = currLeft + currWidth;

        final int prevBaseline = mPrevText.getBaseline();
        final int currBaseline = mCurrText.getBaseline();
        final int nextBaseline = mNextText.getBaseline();
        final int maxBaseline = Math.max(Math.max(prevBaseline, currBaseline), nextBaseline);
        final int prevTopOffset = maxBaseline - prevBaseline;
        final int currTopOffset = maxBaseline - currBaseline;
        final int nextTopOffset = maxBaseline - nextBaseline;
        final int alignedPrevHeight = prevTopOffset + mPrevText.getMeasuredHeight();
        final int alignedCurrHeight = currTopOffset + mCurrText.getMeasuredHeight();
        final int alignedNextHeight = nextTopOffset + mNextText.getMeasuredHeight();
        final int maxTextHeight = Math.max(Math.max(alignedPrevHeight, alignedCurrHeight),
                alignedNextHeight);

        final int vgrav = mGravity & Gravity.VERTICAL_GRAVITY_MASK;

        int prevTop;
        int currTop;
        int nextTop;
        switch (vgrav) {
            default:
            case Gravity.TOP:
                prevTop = paddingTop + prevTopOffset;
                currTop = paddingTop + currTopOffset;
                nextTop = paddingTop + nextTopOffset;
                break;
            case Gravity.CENTER_VERTICAL:
                final int paddedHeight = stripHeight - paddingTop - paddingBottom;
                final int centeredTop = (paddedHeight - maxTextHeight) / 2;
                prevTop = centeredTop + prevTopOffset;
                currTop = centeredTop + currTopOffset;
                nextTop = centeredTop + nextTopOffset;
                break;
            case Gravity.BOTTOM:
                final int bottomGravTop = stripHeight - paddingBottom - maxTextHeight;
                prevTop = bottomGravTop + prevTopOffset;
                currTop = bottomGravTop + currTopOffset;
                nextTop = bottomGravTop + nextTopOffset;
                break;
        }

        mCurrText.layout(currLeft, currTop, currRight,
                currTop + mCurrText.getMeasuredHeight());

        final int prevLeft = Math.min(paddingLeft, currLeft - mScaledTextSpacing - prevWidth);
        mPrevText.layout(prevLeft, prevTop, prevLeft + prevWidth,
                prevTop + mPrevText.getMeasuredHeight());

        final int nextLeft = Math.max(stripWidth - paddingRight - nextWidth,
                currRight + mScaledTextSpacing);
        mNextText.layout(nextLeft, nextTop, nextLeft + nextWidth,
                nextTop + mNextText.getMeasuredHeight());

        mLastKnownPositionOffset = positionOffset;
        mUpdatingPositions = false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Must measure with an exact width");
        }

        this.width = MeasureSpec.getSize(widthMeasureSpec);
        this.height = MeasureSpec.getSize(heightMeasureSpec);

        final int heightPadding = getPaddingTop() + getPaddingBottom();
        final int childHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                heightPadding, LayoutParams.WRAP_CONTENT);

        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int widthPadding = (int) (widthSize * 0.2f);
        final int childWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                widthPadding, LayoutParams.WRAP_CONTENT);

        mPrevText.measure(childWidthSpec, childHeightSpec);
        mCurrText.measure(childWidthSpec, childHeightSpec);
        mNextText.measure(childWidthSpec, childHeightSpec);

        this.prevWidth = mPrevText.getMeasuredWidth();
        this.currWidth = mCurrText.getMeasuredWidth();

        Log.e(TAG, "width = " + this.width + ", height = " + this.height + ", prevWidth = " + this.prevWidth + ", currWidth = " + this.currWidth);

        final int height;
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode == MeasureSpec.EXACTLY) {
            height = MeasureSpec.getSize(heightMeasureSpec);
        } else {
            final int textHeight = mCurrText.getMeasuredHeight();
            final int minHeight = getMinHeight();
            height = Math.max(minHeight, textHeight + heightPadding);
        }

        final int childState = ViewCompat.getMeasuredState(mCurrText);
        final int measuredHeight = ViewCompat.resolveSizeAndState(height, heightMeasureSpec,
                childState << ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
        setMeasuredDimension(widthSize, measuredHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mPager != null) {
            final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
            updateTextPositions(mLastKnownCurrentPage, offset, true);
        }
    }

    int getMinHeight() {
        int minHeight = 0;
        final Drawable bg = getBackground();
        if (bg != null) {
            minHeight = bg.getIntrinsicHeight();
        }
        return minHeight;
    }

    private class PageListener extends DataSetObserver implements ViewPager.OnPageChangeListener,
            ViewPager.OnAdapterChangeListener {
        private int mScrollState;

        PageListener() {
        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if (positionOffset > 0.5f) {
                // Consider ourselves to be on the next page when we're 50% of the way there.
                position++;
            }
            updateTextPositions(position, positionOffset, false);
        }

        @Override
        public void onPageSelected(int position) {
            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
                // Only update the text here if we're not dragging or settling.
                updateText(mPager.getCurrentItem(), mPager.getAdapter());

                final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
                updateTextPositions(mPager.getCurrentItem(), offset, true);
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            mScrollState = state;
        }

        @Override
        public void onAdapterChanged(ViewPager viewPager, PagerAdapter oldAdapter,
                                     PagerAdapter newAdapter) {
            updateAdapter(oldAdapter, newAdapter);
        }

        @Override
        public void onChanged() {
            updateText(mPager.getCurrentItem(), mPager.getAdapter());

            final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
            updateTextPositions(mPager.getCurrentItem(), offset, true);
        }
    }
}
